/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frstat_port.c                                      */
/*  NOTE      =                                                    */
/*  DATE      = 2015/08/07 by guojq                                */
/*******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

#include "frcomm.h"
#include "frmem.h"
#include "frlog.h"
#include "frlist.h"
#include "frhash2.h"

#include "frct_comm.h"
#include "frreport_comm.h"
#include "frreport_mng.h"

typedef struct __ports_stat {
    u_short proto;
    u_int   dst_ip;
    u_short dst_port;
    
    unsigned long ul_active;
    
    unsigned long ul_tx_bytes;
    unsigned long ul_rx_bytes;
    unsigned long ul_tx_pkts;
    unsigned long ul_rx_pkts;
} Frports_stat;

FR_STATIC Frhash_header g_st_hash_stat_ports={0,0,NULL};
FR_STATIC pthread_rwlock_t g_ports_rwlock;

/*******************************************************************************
*  FUNC     :  生成hash用key
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC int __conn_key(u_short proto, u_int ui_dst_ip, u_short us_dst_port
                        , char *p_key, unsigned int ui_key_size)
{
    int i_key_len=0;

    if(p_key == NULL || ui_key_size < sizeof(u_short) *2 + sizeof(u_int) )
        return 0;

    memcpy(p_key+i_key_len, &proto, sizeof(u_short));
    i_key_len += sizeof(u_short);

    memcpy(p_key+i_key_len, &ui_dst_ip, sizeof(u_int));
    i_key_len += sizeof(u_int);

    memcpy(p_key+i_key_len, &us_dst_port, sizeof(u_short));
    i_key_len += sizeof(u_short);

    return i_key_len;
}

/*******************************************************************************
*  FUNC     :  如果连接数为0，删除
*  ARGS     :  i_l7_proto       l7协议名
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __frports_del(Frlist_node *p_st_node_stat)
{
    if(p_st_node_stat == NULL)
        return;

    pthread_rwlock_wrlock(&g_ports_rwlock);
    frhash2_del(&g_st_hash_stat_ports, p_st_node_stat, frlist_node_data_free);
    pthread_rwlock_unlock(&g_ports_rwlock);

    return;
}

/*******************************************************************************
*  FUNC     : 端口信息上报，并删除空节点
*  ARGS     :
*  RTN      :
*  NOTE     : 该函数不能并发调用
*******************************************************************************/
void frstat_ports_report(void)
{
    Frlist_header st_lst_hdr_del, *p_lst_hdr_conn=NULL;
    Frlist_node *p_st_node_stat = NULL, *p_st_node_del=NULL;
    Frports_stat *p_st_ports_stat=NULL;
    Frreport_data *p_st_report_data=NULL;
    Frreport_body_data *p_st_report_body=NULL;
    int i_loop = 0;
    int i_has_report_data = FR_NO;

    //new report
    p_st_report_data = frreport_data_stat_port_new();
    if(p_st_report_data == NULL)
        return;

    
    frlist_header_init(&st_lst_hdr_del);
    //没有删除操作，hash表不加锁
    FRHASH_LOOP(i_loop, &g_st_hash_stat_ports, p_lst_hdr_conn)
    {
        if(p_lst_hdr_conn == NULL)
            continue;
        p_st_node_stat = FRLIST_FIRST_NODE(p_lst_hdr_conn);
        while(p_st_node_stat)
        {
            p_st_ports_stat =  (Frports_stat*)FRLIST_GET_NODE_DATA(p_st_node_stat);
            if(p_st_ports_stat)
            {
                //数据上报
                if(p_st_ports_stat->ul_active
                   || p_st_ports_stat->ul_tx_pkts || p_st_ports_stat->ul_rx_pkts
                   || p_st_ports_stat->ul_tx_bytes || p_st_ports_stat->ul_rx_bytes)
                {
                    p_st_report_body = frreport_body_new_stat_port(p_st_ports_stat->proto
                                                                , p_st_ports_stat->dst_ip
                                                                , p_st_ports_stat->dst_port
                                                                , p_st_ports_stat->ul_tx_bytes
                                                                , p_st_ports_stat->ul_rx_bytes
                                                                , p_st_ports_stat->ul_tx_pkts
                                                                , p_st_ports_stat->ul_rx_pkts);
                    frreport_data_add_body(p_st_report_data, p_st_report_body);
                    i_has_report_data = FR_YES;
                }

                if(p_st_ports_stat->ul_active == 0
                   && p_st_ports_stat->ul_tx_pkts == 0 && p_st_ports_stat->ul_rx_pkts == 0
                   && p_st_ports_stat->ul_tx_bytes == 0 && p_st_ports_stat->ul_rx_bytes == 0)
                {
                    p_st_node_del = frlist_node_create(p_st_node_stat);
                    if(p_st_node_del)
                        frlist_push(&st_lst_hdr_del, p_st_node_del);
                }

                __sync_lock_release(&p_st_ports_stat->ul_tx_pkts);
                __sync_lock_release(&p_st_ports_stat->ul_rx_pkts);
                __sync_lock_release(&p_st_ports_stat->ul_tx_bytes);
                __sync_lock_release(&p_st_ports_stat->ul_rx_bytes);
                __sync_lock_release(&p_st_ports_stat->ul_active);
            }
            p_st_node_stat = FRLIST_NEXT_NODE(p_st_node_stat);
        }
    }
    //start report
    if(i_has_report_data == FR_YES)
        frreport_mng_save_report_data2(&p_st_report_data);
    else
        frreport_data_free(&p_st_report_data);

    //删除空节点
    if(frlist_size(&st_lst_hdr_del) <=0)
        return;

    p_st_node_del = frlist_pop(&st_lst_hdr_del);
    while(p_st_node_del)
    {
        p_st_node_stat =  (Frlist_node*)FRLIST_GET_NODE_DATA(p_st_node_del);
        __frports_del(p_st_node_stat);
        frfree(p_st_node_del);
        p_st_node_del = frlist_pop(&st_lst_hdr_del);
    }

    return;
}

/*******************************************************************************
*  FUNC     :  增加连接数统计
*  ARGS     :  ui_src_ip        原ip
*           :  ui_dst_ip        目的ip
*           :  us_dst_port      目的port
*           :  i_conns          连接数据(增加:1，关闭:-1)
*  RTN      :  FR_SUCC   (成功)
*              FR_FAIL   (失败)
*  NOTE     :  
*******************************************************************************/
int frstat_ports_add_stat(u_short proto, u_int ui_dst_ip, u_short us_dst_port
                        , unsigned int ui_tx_bytes, unsigned int ui_rx_bytes
                        , unsigned int ui_tx_pkts, unsigned int ui_rx_pkts)
{
    Frlist_node *p_st_node = NULL;
    Frlist_node *p_st_new_node = NULL;
    Frports_stat *p_st_conn_stat=NULL;
    char arr_key[16];
    int i_key_len=0;
    
    i_key_len = __conn_key(proto, ui_dst_ip, us_dst_port, arr_key, sizeof(arr_key));
    if(i_key_len <=0)
        return FR_FAIL;

    pthread_rwlock_wrlock(&g_ports_rwlock);
    p_st_node = frhash2_getStr(&g_st_hash_stat_ports, arr_key, i_key_len);
    if (p_st_node && p_st_node->data)
    {
        p_st_conn_stat = (Frports_stat *)p_st_node->data;
        
        __sync_fetch_and_add(&p_st_conn_stat->ul_active, 1);
        if(ui_tx_bytes > 0)
            __sync_fetch_and_add(&p_st_conn_stat->ul_tx_bytes, ui_tx_bytes);
        if(ui_rx_bytes > 0)
            __sync_fetch_and_add(&p_st_conn_stat->ul_rx_bytes, ui_rx_bytes);
        if(ui_tx_pkts > 0)
            __sync_fetch_and_add(&p_st_conn_stat->ul_tx_pkts, ui_tx_pkts);
        if(ui_rx_pkts > 0)
            __sync_fetch_and_add(&p_st_conn_stat->ul_rx_pkts, ui_rx_pkts);
        pthread_rwlock_unlock(&g_ports_rwlock);
        return FR_SUCC;
    }

    p_st_conn_stat = (Frports_stat *) frmalloc(sizeof(Frports_stat));
    if (p_st_conn_stat == NULL)
    {
        pthread_rwlock_unlock(&g_ports_rwlock);
        FRLOG_ERROR_DETAIL("frmalloc err");
        return FR_FAIL;
    }
    memset(p_st_conn_stat, 0x00, sizeof(Frports_stat));
    
    p_st_conn_stat->proto = proto;
    p_st_conn_stat->dst_ip = ui_dst_ip;
    p_st_conn_stat->dst_port = us_dst_port;
    __sync_fetch_and_add(&p_st_conn_stat->ul_active, 1);
    if(ui_tx_bytes > 0)
        __sync_fetch_and_add(&p_st_conn_stat->ul_tx_bytes, ui_tx_bytes);
    if(ui_rx_bytes > 0)
        __sync_fetch_and_add(&p_st_conn_stat->ul_rx_bytes, ui_rx_bytes);
    if(ui_tx_pkts > 0)
        __sync_fetch_and_add(&p_st_conn_stat->ul_tx_pkts, ui_tx_pkts);
    if(ui_rx_pkts > 0)
        __sync_fetch_and_add(&p_st_conn_stat->ul_rx_pkts, ui_rx_pkts);
    
    p_st_new_node = frlist_node_create(p_st_conn_stat);
    if (p_st_new_node == NULL)
    {
        frfree(p_st_conn_stat);
        pthread_rwlock_unlock(&g_ports_rwlock);
        FRLOG_ERROR_DETAIL("frlist_node_create err");
        return FR_FAIL;
    }
    
    frhash2_addStr(&g_st_hash_stat_ports, arr_key, i_key_len, p_st_new_node);
    pthread_rwlock_unlock(&g_ports_rwlock);
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  初始化端口统计
*  ARGS     :  
*  RTN      :  FR_SUCC   (成功)
*              FR_FAIL   (失败)
*  NOTE     :  
*******************************************************************************/
int frstat_ports_init(unsigned long ul_hash_size)
{
    if (pthread_rwlock_init(&g_ports_rwlock, NULL) != 0)
    {
        FRLOG_ERROR_DETAIL("pthread_rwlock_init failed");
        return FR_FAIL;
    }

    if (frhash2_create(&g_st_hash_stat_ports, ul_hash_size) != FR_SUCC)
    {
        FRLOG_ERROR_DETAIL("frhash2_create failed");
        pthread_rwlock_destroy(&g_ports_rwlock);
        return FR_FAIL;
    }

    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  退出端口统计
*  ARGS     :  
*  RTN      :  
*  NOTE     :  
*******************************************************************************/
void frstat_ports_destroy(void)
{
    pthread_rwlock_wrlock(&g_ports_rwlock);
    frhash2_free(&g_st_hash_stat_ports, frlist_node_data_free);
    pthread_rwlock_unlock(&g_ports_rwlock);
    
    pthread_rwlock_destroy(&g_ports_rwlock);

    return;
}
